﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using FluentPS.Consts;
using FluentPS.Enums;
using FluentPS.Extensions;
using System.Web.Services.Protocols;
using FluentPS.Mapping;
using FluentPS.WebServiceProxy;
using FluentPS.WebSvcProject;

namespace FluentPS.Services.Impl
{
    public class PSProjectService<T>: IPSProjectService<T> where T:class, new()
    {
        internal const string WebServiceUrl = "project.asmx";
        private readonly ILogService _logService;
        private readonly ISessionService _sessionService;
        private readonly DataStoreEnum DataStore = DataStoreEnum.WorkingStore;
        private readonly IProjectProxy _wssProject;

        public PSProjectService(ILogService logService, ISessionService sessionService, IProjectProxy wssProject)
        {
            _logService = logService;
            _sessionService = sessionService;
            _wssProject = wssProject;

            Init();
        }

        private void Init()
        {            
            _wssProject.Url = _sessionService.PsiBaseUrl + WebServiceUrl;
            _wssProject.UseDefaultCredentials = true;
        }

        public T Get(Guid entityUid)
        {
            return Get(entityUid, true);
        }

        public T Get(Guid entityUid, bool loadCustomFields)
        {
            try
            {
                int dataFlags = (int)ProjectEntityType.Project;
                if (loadCustomFields)
                    dataFlags |= (int)ProjectEntityType.ProjectCustomFields;
                using (DataSet dsProject = _wssProject.ReadProjectEntities(entityUid, dataFlags, DataStore))
                {
                    if (dsProject != null && dsProject.Tables[PsDataTableNames.Project].Rows.Count > 0)
                        return ReadProjectFromDataSet(entityUid, dsProject, loadCustomFields);
                }
            }
            catch (SoapException ex)
            {
                _logService.Log("Read project exception: " + ex.Message);
            }
            return null;
        }

        public T ReadProjectFromDataSet(Guid projectUid, DataSet dsProject, bool loadCustomFields)
        {
            return ReadProjectFromDataSet(projectUid, dsProject, loadCustomFields, DataRowVersion.Current);
        }

        public T ReadProjectFromDataSet(Guid projectUid, DataSet dsProject, bool loadCustomFields, DataRowVersion dataRowVersion)
        {
            if (dsProject == null || !dsProject.Tables.Contains(PsDataTableNames.Project) || dsProject.Tables[PsDataTableNames.Project].Rows.Count == 0)
                return null;

            DataRow projectRow = dsProject.Tables[PsDataTableNames.Project].Rows[0];

            var project = new T();
            var entityDataMapper = new EntityDataMapper<T>(_logService);
            entityDataMapper.MapDataRowToEntityNativeFields(projectRow, project, dataRowVersion);

            if (loadCustomFields)
                entityDataMapper.MapCustomFieldsToEntity(project, projectUid, dsProject.Tables[PsDataTableNames.ProjectCustomFields], dataRowVersion, new ProjectCustomFieldsMappingInfo());

            return project;
        }

        public Guid Update(T entity, Guid sessionId)
        {
            var propertiesToUpdate = new List<PropertyInfo>();
            typeof(T).ForeachProperty(propertiesToUpdate.Add);

            var entityDataMapper = new EntityDataMapper<T>(_logService);
            var entityUid = entityDataMapper.GetEntityUid<Guid>(entity);
            if (entityUid == Guid.Empty)
                throw new Exception("Invalid entity UID");

            using (var dsProject = _wssProject.ReadProjectEntities(entityUid, (int)ProjectEntityType.Project | (int)ProjectEntityType.ProjectCustomFields, DataStore))
            {
                if (dsProject.Tables[PsDataTableNames.Project].Rows.Count > 0)
                {
                    entityDataMapper.MapEntityNativeFieldsToDataRow(entity, dsProject.Tables[PsDataTableNames.Project].Rows[0]);
                    entityDataMapper.MapCustomFieldsToDataSet(entity, entityUid, dsProject.Tables[PsDataTableNames.ProjectCustomFields], propertiesToUpdate, new ProjectCustomFieldsMappingInfo());
                }

                Guid jobId = Guid.NewGuid();
                _wssProject.QueueUpdateProject(jobId, sessionId, dsProject, false);
                return jobId;
            }
        }

        public Guid CheckIn(Guid projectUid, Guid sessionId)
        {
            try
            {
                Guid jobId = Guid.NewGuid();
                _wssProject.QueueCheckInProject(jobId, projectUid, true, sessionId, string.Empty);
                return jobId;
            }
            catch (Exception ex)
            {
                _logService.Log("Checking in project exception:" + ex.Message);
                throw;
            }
        }

        public Guid Publish(Guid projectUid, bool createWorkSpace)
        {
            try
            {
                Guid publishJobId = Guid.NewGuid();
                _wssProject.QueuePublish(publishJobId, projectUid, true, (createWorkSpace) ? string.Empty : null);
                return publishJobId;
            }
            catch (Exception ex)
            {
                _logService.Error("Publish project exception: " + ex.Message);
                throw;
            }
        }

        public void CheckOut(Guid projectUid, Guid sessionUid)
        {
            try
            {
                _wssProject.CheckOutProject(projectUid, sessionUid, string.Empty);
            }
            catch (Exception ex)
            {
                _logService.Error("Check out project exception: " + ex.Message);
                throw;
            }
        }

    }
}
